<template>
  <div class="frame" style="padding: 0">
    <div v-if="isPage1" class="background_image case page1">
      <div class="content_box">
        <h1>蓝擎 国内领先数据要素化引擎</h1>
        <h2>发掘数据中的价值</h2>
      </div>
      <div class="four_display">
        <div style="display: flex; align-items: center;">
          <div style="width: 2px; height: 100px; background-color: rgb(47, 107, 236); margin-right: 10px;"></div>
          <div class="one_display">
            <span style="font-size: 30px; color: rgb(47, 107, 236); font-family: 'PingFang SC', sans-serif;">{{ yearNum }}年</span>
            <p style="font-size: 20px; color: rgb(47, 107, 236); font-family: 'PingFang SC', sans-serif;">深耕自然语言处理</p>
          </div>
          <div style="width: 2px; height: 100px; background-color: rgb(47, 107, 236); margin-right: 10px;"></div>
          <div class="one_display">
            <span style="font-size: 30px; color: rgb(47, 107, 236); font-family: 'PingFang SC', sans-serif;">{{ patentNum }}项</span>
            <p style="font-size: 20px; color: rgb(47, 107, 236); font-family: 'PingFang SC', sans-serif;">国内外发明专利</p>
          </div>
          <div style="width: 2px; height: 100px; background-color: rgb(47, 107, 236); margin-right: 10px;"></div>
          <div class="one_display">
            <span style="font-size: 30px; color: rgb(47, 107, 236); font-family: 'PingFang SC', sans-serif;">{{ paperNum }}篇</span>
            <p style="font-size: 20px; color: rgb(47, 107, 236); font-family: 'PingFang SC', sans-serif;">国内外顶尖学术论文</p>
          </div>
          <div style="width: 2px; height: 100px; background-color: rgb(47, 107, 236); margin-right: 10px;"></div>
          <div class="one_display">
            <span style="font-size: 30px; color: rgb(47, 107, 236); font-family: 'PingFang SC', sans-serif;">{{ awardNum }}项</span>
            <p style="font-size: 20px; color: rgb(47, 107, 236); font-family: 'PingFang SC', sans-serif;">顶级竞赛奖项</p>
          </div>
        </div>
      </div>
    </div>

    <div v-if="!isPage1" class="case page2">
      <div class="page1_txbox">
        <p class="yonghu">用户，</p><p class="nihao">你好。</p>
        <p class="can_help">我能帮你做点什么？</p>
      </div>

      <!-- 模型库选择框 -->
      <div class="big_box">
        <!-- NER 模型选择 -->
        <div class="little_box">
          <h2>实体要素抽取模型库</h2>
          <el-radio-group v-model="selectedNERModel" @change="selectModelNER">
            <el-radio
              v-for="model in nerModels"
              :key="model"
              :label="model">
              {{ model }}
            </el-radio>
          </el-radio-group>
        </div>

        <!-- RE 模型选择 -->
        <div class="little_box">
          <h2>关系要素抽取模型库</h2>
          <el-radio-group v-model="selectedREModel" @change="selectModelRE">
            <el-radio
              v-for="model in reModels"
              :key="model"
              :label="model">
              {{ model }}
            </el-radio>
          </el-radio-group>
        </div>

        <!-- EE 模型选择 -->
        <div class="little_box">
          <h2>事件要素抽取模型库</h2>
          <el-radio-group v-model="selectedEEModel" @change="selectModelEE">
            <el-radio
              v-for="model in eeModels"
              :key="model"
              :label="model">
              {{ model }}
            </el-radio>
          </el-radio-group>
        </div>
      </div>

      <div v-if="activeModel" class="model_box">
        <div class="model_header">
          <h2>{{ activeModel }}</h2>
          <button @click="closeModel" style="color: black;">✖</button>
        </div>
        <div class="model_content">

          <!-- NER 模型库 -->
          <div v-if="activeModel === 'NER模型库'" class="list-section">
            <ul>
              <li
                v-for="(item, index) in paginatedNER"
                :key="index"
                @click="selectModel(item, 'NER')"
                :class="{'selected-model': selectedNERModel === item}">
                {{ item }}
              </li>
            </ul>
            <div class="pagination">
              <button :disabled="currentNERPage === 1" @click="prevNERPage">上一页</button>
              <span>页数 {{ currentNERPage }}/{{ totalNERPages }}</span>
              <button :disabled="currentNERPage === totalNERPages" @click="nextNERPage">下一页</button>
            </div>
          </div>

          <!-- RE 模型库 -->
          <div v-if="activeModel === 'RE模型库'" class="list-section">
            <ul>
              <li
                v-for="(item, index) in paginatedRE"
                :key="index"
                @click="selectModel(item, 'RE')"
                :class="{'selected-model': selectedREModel === item}">
                {{ item }}
              </li>
            </ul>
            <div class="pagination">
              <button :disabled="currentREPage === 1" @click="prevREPage">上一页</button>
              <span>页数 {{ currentREPage }}/{{ totalREPages }}</span>
              <button :disabled="currentREPage === totalREPages" @click="nextREPage">下一页</button>
            </div>
          </div>

          <!-- EE 模型库 -->
          <div v-if="activeModel === 'EE模型库'" class="list-section">
            <ul>
              <li
                v-for="(item, index) in paginatedEE"
                :key="index"
                @click="selectModel(item, 'EE')"
                :class="{'selected-model': selectedEEModel === item}">
                {{ item }}
              </li>
            </ul>
            <div class="pagination">
              <button :disabled="currentEEPage === 1" @click="prevEEPage">上一页</button>
              <span>页数 {{ currentEEPage }}/{{ totalEEPages }}</span>
              <button :disabled="currentEEPage === totalEEPages" @click="nextEEPage">下一页</button>
            </div>
          </div>

        </div>
      </div>

      <!-- 搜索栏 -->
      <div class="search-bar">
        <input
          v-model="userInput"
          type="text"
          class="search-input"
          placeholder="请输入内容"
        />
        <button class="search-button" @click="startModel">启动</button>
      </div>

      <!-- 模型结果展示 -->
      <div v-if="showResultPopup" class="result-popup">
        <div class="result-popup-header">
          <h2>实体要素抽取结果</h2>
          <button @click="closeResultPopup" class="result-popup-close">✖</button>
        </div>
        <div class="result-popup-original">
          <p><strong>原始句子:</strong> {{ userInput }}</p>
        </div>

        <div class="result-popup-content">
          <paragraph v-if="modelResult" :paragraph="modelResult"/>
        </div>

        <!-- 添加知识图谱展示部分 -->
        <v-chart :option="chartOption" style="height: 500px; width: 100%; margin-top: 20px;"></v-chart>
      </div>

      <!-- 事件结果展示弹窗 -->
      <div v-if="showEventResult" class="event-result-popup">
        <div class="event-popup-header">
          <h2>事件要素抽取结果</h2>
          <button @click="closeEventResultPopup" class="event-popup-close">✖</button>
        </div>
        <div class="result-popup-original">
          <p><strong>原始句子:</strong> {{ userInput }}</p>
        </div>
        <div class="event-popup-content">
          <div v-for="(event, index) in eventResult" :key="event.id" class="event-item">
            <p>
              <strong>事件{{ index + 1 }}</strong>：event_type：{{ event.event_type }}；trigger：{{ event.trigger }}；arguments：
              [
              <span v-for="(arg, idx) in event.arguments" :key="idx">
          {argument: {{ arg.argument }}, role: {{ arg.role }}}<span v-if="idx < event.arguments.length - 1">, </span>
        </span>
              ]
            </p>
          </div>
        </div>
        <!-- 知识图谱展示部分，单一图谱 -->
        <v-chart :option="chartOption" style="height: 800px; width: 100%; margin-top: 20px;"></v-chart>
      </div>


      <!-- 关系结果展示弹窗 -->
      <div v-if="showREResult" class="re-result-popup">
        <div class="result-popup-header">
          <h2>关系要素抽取结果</h2>
          <button @click="closeREResultPopup" class="result-popup-close">✖</button>
        </div>
        <div class="result-popup-original">
          <p><strong>原始句子:</strong> {{ userInput }}</p>
        </div>
        <div class="result-popup-content">
          <span
            v-for="(relation, index) in reResult"
            :key="index"
            class="triplet"
            :style="{ color: getRandomColor() }"
          >
            ({{ relation.start_entity }}, {{ relation.end_entity }}, {{ relation.type }})&#09;
          </span>
        </div>
        <!-- 添加 ref 属性 -->
        <v-chart ref="reChart" :option="reChartOption" style="height: 500px; width: 100%; margin-top: 20px;"></v-chart>
      </div>












    </div>

    <!-- 切换按钮 -->
    <div class="switch-buttons">
      <!-- 右箭头 (位于 page1) -->
      <img
        v-if="isPage1"
        class="switch-icon right-arrow"
        src="@/assets/images/headpage_img/right_anniu.png"
        alt="Right Arrow"
        @click="togglePage"
      />

      <!-- 左箭头 (位于 page2) -->
      <img
        v-if="!isPage1"
        class="switch-icon left-arrow"
        src="@/assets/images/headpage_img/left_anniu.png"
        alt="Left Arrow"
        @click="togglePage"
      />
    </div>
  </div>
</template>

<script>


import Vue from 'vue';
import ECharts from 'vue-echarts';
import 'echarts';
import { RadioGroup, Radio } from 'element-ui'; // 引入单选框组件

Vue.component('v-chart', ECharts);
Vue.use(RadioGroup);
Vue.use(Radio);



const text = {
  name: "text",
  props: {
    segment: {
      type: Object,
      required: true
    }
  },
  render(createElement) {
    return createElement(
      'span',
      {
        class: {
          'text': true
        }
      },
      [
        this.segment.text
      ]
    )
  }
};

const entity = {
  name: "entity",
  props: {
    segment: {
      type: Object,
      required: true
    },
    first: {
      type: Boolean,
      default: false
    }
  },
  components: {
    'u-text': text
  },
  render(createElement) {
    let style = {};
    if (this.first) {
      let deep = this.calcDeep(this.segment, 0);
      style.marginBottom = `calc(${deep}rem + 0.45rem)`;
    }
    return createElement(
      'div',
      {
        class: {
          entity: true,
          last: this.all(this.segment.segment, i => i.type === 'text'),
        },
        style: {
          ...style,
          color: this.segment.entity.color,
        },
        attrs: {
          text: this.segment.entity.type
        }
      },
      this.segment.segment.map(i => this.createElement(createElement, i))
    )
  },
  methods: {
    all(segments, call) {
      return segments.every(call);
    },
    createElement(h, segment) {
      if (segment.type === 'text')
        return h(
          'u-text',
          {
            props: {
              segment
            }
          }
        )
      return h(
        'div',
        {
          class: {
            entity: true,
            last: this.all(segment.segment, i => i.type === 'text'),
          },
          style: {
            color: segment.entity.color,
            text: segment.entity.type
          },
          attrs: {
            text: segment.entity.type
          }
        },
        segment.segment.map(i => this.createElement(h, i))
      )
    },
    calcDeep(tree, deep) {
      if (tree.type === 'entity') {
        return Math.max(...tree.segment.map(i => this.calcDeep(i, deep + 1)));
      }
      return deep;
    }
  }
};

const paragraph = {
  name: "paragraph",
  props: {
    paragraph: {
      type: Object,
      required: true
    }
  },
  components: {
    entity,
    'u-text': text
  },
  render(createElement) {
    return createElement(
      'p',
      {
        class: {
          paragraph: true
        }
      },
      this.paragraph.segment.map(segment => {
        if (segment.type === 'text')
          return createElement(
            'u-text',
            {
              props: {
                segment
              }
            }
          )
        return createElement(
          'entity',
          {
            props: {
              segment,
              first: true
            }
          }
        )
      })
    )
  },
};


export default {
  name: "headpage",
  components: {
    paragraph,
    'v-chart': ECharts, // 注册 ECharts 组件
    // 注册 Element UI 单选框组件
    'el-radio-group': RadioGroup,
    'el-radio': Radio,
    // ...其他组件
  },
  data() {
    return {
      yearNum: 0,
      patentNum: 0,
      paperNum: 0,
      awardNum: 0,
      isPage1: true, // 用于控制当前页面

      activeModel: null,
      selectedModel: null, // 初始值为 null，表示没有选中任何模型

      nerModels: ["NER模型1", "NER模型2", "NER模型3", "NER模型4"],
      reModels: ["RE模型1", "RE模型2", "RE模型3", "RE模型4"],
      eeModels: ["EE模型1", "EE模型2", "EE模型3", "EE模型4"],
      currentNERPage: 1,
      currentREPage: 1,
      currentEEPage: 1,
      pageSize: 3,

      // ... 其他数据
      allModelData: [], // 保存接口返回的所有模型数据
      userInput: '', // 输入框的内容
      modelApi: '', // 选择的模型的 API
      modelResult: null, // 存储请求返回的模型结果

      // ... 其他数据
      selectedNERModel: null,  // 当前选中的 NER 模型
      selectedREModel: null,   // 当前选中的 RE 模型
      selectedEEModel: null,   // 当前选中的 EE 模型


      showResultPopup: false, // 控制弹窗显示状态

      // 新增：事件抽取相关
      eventResult: null, // 存储事件抽取结果
      showEventResult: false, // 控制事件结果弹窗显示状态

      reResult: null,          // 存储关系抽取结果
      showREResult: false,     // 控制关系抽取弹窗的显示状态
      reChartOption: null,     // ECharts 的配置对象用于关系抽取图谱


      entityType2Color: {
        'ORG': '#9b59b6',
        'PER': '#3498db',
        'FAC': '#e67e22',
        'VEH': '#16a085',
        'LOC': '#c0392b',
        'WEA': '#8c7ae6',
        'GPE': '#e1b12c',
      },

      chartOption: null, // ECharts 的配置对象

    };
  },
  computed: {
    totalNERPages() {
      return Math.ceil(this.nerModels.length / this.pageSize);
    },
    paginatedNER() {
      const start = (this.currentNERPage - 1) * this.pageSize;
      const end = start + this.pageSize;
      return this.nerModels.slice(start, end);
    },
    totalREPages() {
      return Math.ceil(this.reModels.length / this.pageSize);
    },
    paginatedRE() {
      const start = (this.currentREPage - 1) * this.pageSize;
      const end = start + this.pageSize;
      return this.reModels.slice(start, end);
    },
    totalEEPages() {
      return Math.ceil(this.eeModels.length / this.pageSize);
    },
    paginatedEE() {
      const start = (this.currentEEPage - 1) * this.pageSize;
      const end = start + this.pageSize;
      return this.eeModels.slice(start, end);
    },
  },
  mounted() {
    this.fetchData();
  },
  methods: {

    prevNERPage() {
      if (this.currentNERPage > 1) this.currentNERPage--;
    },
    nextNERPage() {
      if (this.currentNERPage < this.totalNERPages) this.currentNERPage++;
    },
    prevREPage() {
      if (this.currentREPage > 1) this.currentREPage--;
    },
    nextREPage() {
      if (this.currentREPage < this.totalREPages) this.currentREPage++;
    },
    prevEEPage() {
      if (this.currentEEPage > 1) this.currentEEPage--;
    },
    nextEEPage() {
      if (this.currentEEPage < this.totalEEPages) this.currentEEPage++;
    },

    // 切换页面显示
    togglePage() {
      this.isPage1 = !this.isPage1;
    },
    async fetchData() {
      try {
        const response = await fetch(process.env.VUE_APP_BASE_API + '/system/headPaper/getInfo');
        const data = await response.json();
        this.yearNum = data.data.currentYear;
        this.patentNum = data.data.patentNum;
        this.paperNum = data.data.paperNum;
        this.awardNum = data.data.awardNum;
      } catch (error) {
        console.error("首页1获取后端数据出错:", error);
      }

      try {
        const modelResponse = await fetch(process.env.VUE_APP_BASE_API + '/system/headPaper/getModelList');
        const modelData = await modelResponse.json();

        if (modelData.code === 200 && modelData.rows) {
          // 保存所有接口数据
          this.allModelData = modelData.rows;

          // 根据任务英文名过滤模型列表并保存完整数据
          const nerModelData = this.allModelData.find(item => item.taskEnName === 'NER');
          const reModelData = this.allModelData.find(item => item.taskEnName === 'RE');
          const eeModelData = this.allModelData.find(item => item.taskEnName === 'EE');

          // 只提取模型名称
          this.nerModels = nerModelData ? nerModelData.modelList.map(model => model.modelName) : [];
          this.reModels = reModelData ? reModelData.modelList.map(model => model.modelName) : [];
          this.eeModels = eeModelData ? eeModelData.modelList.map(model => model.modelName) : [];
        } else {
          console.error("获取模型列表失败:", modelData.msg || "未知错误");
        }
      } catch (error) {
        console.error("获取模型列表数据出错:", error);
      }
    },

    tokens2sentence(tokens) {
      return tokens.join('');
    },
    processE(data) {
      const { sentence, ner } = data;
      const sortedNer = ner.sort((a, b) => {
        if (a.index[0] !== b.index[0])
          return a.index[0] - b.index[0];
        return b.index[1] - a.index[1];
      });
      const ret = {
        text: sentence.join(''),
        segment: []
      };
      let start = 0, idx = { idx: 0 };
      let current = null;
      while (idx.idx < sortedNer.length) {
        current = sortedNer[idx.idx];
        if (start !== current.index[0])
          ret.segment.push({
            text: this.tokens2sentence(sentence.slice(start, current.index[0])),
            type: 'text'
          });
        ret.segment.push(this.process(sentence, sortedNer, idx, current.index[0], current.index[1]));
        start = current.index[1] + 1;
      }
      if (start < sentence.length)
        ret.segment.push({
          text: this.tokens2sentence(sentence.slice(start, sentence.length)),
          type: 'text'
        });
      return ret;
    },
    process(sentence, ner, idx, start, end) {
      const now = ner[idx.idx];
      const entity = {
        type: 'entity',
        entity: {
          type: now.type,
          color: this.entityType2Color[now.type] || '#000', // 颜色映射
        },
        segment: []
      };
      idx.idx += 1;
      while (idx.idx < ner.length) {
        let current = ner[idx.idx];
        if (!(start <= current.index[0] && current.index[1] <= end))
          break;
        if (start !== current.index[0])
          entity.segment.push({
            text: this.tokens2sentence(sentence.slice(start, current.index[0])),
            type: 'text'
          });
        entity.segment.push(this.process(sentence, ner, idx, current.index[0], current.index[1]));
        start = current.index[1] + 1;
      }
      if (start <= end) {
        entity.segment.push({
          text: this.tokens2sentence(sentence.slice(start, end + 1)),
          type: 'text'
        });
      }
      return entity;
    },

    // 点击启动按钮时调用
    async startModel() {
      if (!this.userInput) {
        alert('请输入内容');
        return;
      }

      if (!this.selectedModel) {
        alert('请选择模型');
        return;
      }

      if (!this.modelApi) {
        alert('请选择模型');
        return;
      }

      try {
        const response = await fetch(process.env.VUE_APP_BASE_API + '/system/headPaper/getModelResult', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            taskName: this.selectedModel,
            modelApi: this.modelApi,
            input: this.userInput
          })
        });

        const data = await response.json();

        if (data.code === 200) {
          if (this.selectedModel === '实体要素抽取') {
            // 处理实体提取结果
            const processedData = this.prepareDataForVisualization(data.data);
            this.modelResult = this.processE(processedData);
            this.originalEntities = data.data; // 存储原始实体数据
            this.processGraphData();
            this.showResultPopup = true;
          } else if (this.selectedModel === '事件要素抽取') {
            // 处理事件提取结果
            this.eventResult = data.data;
            this.showEventResult = true;
            this.processEventGraphData(); // 处理事件图谱
          } else if (this.selectedModel === '关系要素抽取') {
            // 处理关系提取结果
            this.reResult = data.data;
            this.processREGraphData(); // 处理关系图谱
            this.showREResult = true;
          }
        } else {
          console.error('模型请求失败:', data.msg);
        }
      } catch (error) {
        console.error('请求模型结果失败:', error);
      }
    },

    // 关闭事件结果弹窗
    closeEventResultPopup() {
      this.showEventResult = false;
      this.eventResult = null;
    },

    // 处理事件图谱数据
    processEventGraphData() {
      if (!this.eventResult || !Array.isArray(this.eventResult)) return;

      const nodes = [];
      const links = [];
      const categories = []; // 存储所有事件类别
      const nodeMap = new Map(); // 跟踪已添加的节点，防止重复

      // 遍历每个事件
      this.eventResult.forEach((event, eventIndex) => {
        const eventColor = this.getRandomColor();
        const eventId = event.id;
        const eventName = `事件${eventIndex + 1}`; // 生成事件名称，如事件1、事件2

        // 添加事件类别到 categories
        categories.push({
          name: eventName,
          itemStyle: {
            color: eventColor, // 每个事件一个唯一颜色
          },
        });

        // 添加中心事件节点
        const eventNode = {
          name: eventName,
          category: eventName,
          symbolSize: 60,
          itemStyle: {
            color: eventColor,
          },
          label: {
            show: true,
            formatter: '{b}',
            fontSize: 14,
          },
        };
        nodes.push(eventNode);
        nodeMap.set(eventName, true);

        // 添加 event_type 节点
        const eventTypeNodeName = `${eventName}_EventType_${event.event_type}`;
        if (!nodeMap.has(eventTypeNodeName)) {
          nodes.push({
            name: eventTypeNodeName,
            category: eventName, // 同一事件的节点属于同一类别
            symbolSize: 40,
            itemStyle: {
              color: eventColor,
            },
            label: {
              show: true,
              formatter: event.event_type,
              fontSize: 12,
            },
          });
          nodeMap.set(eventTypeNodeName, true);
        }

        // 添加 event_type 连线
        links.push({
          source: eventName,
          target: eventTypeNodeName,
          label: {
            show: true,
            formatter: 'event_type',
            fontSize: 12,
          },
          lineStyle: {
            type: 'solid',
            color: eventColor,
          },
        });

        // 添加 trigger 节点
        const triggerNodeName = `${eventName}_Trigger_${event.trigger}`;
        if (!nodeMap.has(triggerNodeName)) {
          nodes.push({
            name: triggerNodeName,
            category: eventName,
            symbolSize: 40,
            itemStyle: {
              color: eventColor,
            },
            label: {
              show: true,
              formatter: event.trigger,
              fontSize: 12,
            },
          });
          nodeMap.set(triggerNodeName, true);
        }

        // 添加 trigger 连线
        links.push({
          source: eventName,
          target: triggerNodeName,
          label: {
            show: true,
            formatter: 'trigger',
            fontSize: 12,
          },
          lineStyle: {
            type: 'solid',
            color: eventColor,
          },
        });

        // 处理 arguments
        event.arguments.forEach((arg, argIndex) => {
          const argumentNodeName = `${eventName}_Argument_${arg.role}_${arg.argument}`;
          if (!nodeMap.has(argumentNodeName)) {
            nodes.push({
              name: argumentNodeName,
              category: eventName,
              symbolSize: 40,
              itemStyle: {
                color: eventColor,
              },
              label: {
                show: true,
                formatter: arg.argument,
                fontSize: 12,
              },
            });
            nodeMap.set(argumentNodeName, true);
          }

          // 添加 argument 连线
          links.push({
            source: eventName,
            target: argumentNodeName,
            label: {
              show: true,
              formatter: `argument-${arg.role}`,
              fontSize: 12,
            },
            lineStyle: {
              type: 'solid',
              color: eventColor,
            },
          });
        });
      });

      // 配置 ECharts 的图表选项
      this.chartOption = {
        tooltip: {
          formatter: function (params) {
            if (params.dataType === 'node') {
              return `${params.data.name}`;
            }
            return '';
          },
        },
        legend: {
          data: categories.map(cat => cat.name), // 图例为所有事件名称
          top: 'bottom',
        },
        series: [
          {
            name: '事件要素知识图谱',
            type: 'graph',
            layout: 'force',
            categories: categories, // 使用动态生成的类别
            data: nodes,
            links: links,
            roam: true,
            label: {
              position: 'right',
              formatter: '{b}',
            },
            force: {
              repulsion: 3000,
              edgeLength: [100, 200],
            },
            lineStyle: {
              color: '#aaa',
              width: 1,
              curveness: 0,
            },
          },
        ],
      };
    },

    // ...其他 methods

    // 辅助方法：生成随机颜色
    getRandomColor() {
      const letters = '0123456789ABCDEF';
      let color = '#';
      for (let i = 0; i < 6; i++) {
        color += letters[Math.floor(Math.random() * 16)];
      }
      return color;
    },

    // ...之前的 methods
    prepareDataForVisualization(entities) {
      // 将原始输入句子拆分为字符数组
      const sentence = this.userInput.split('');
      // 将实体列表转换为符合 processE 方法要求的 ner 列表
      const ner = entities.map(entity => {
        return {
          index: entity.index,
          type: entity.type
        };
      });
      return {
        sentence,
        ner
      };
    },


    // ...之前的 methods

    processGraphData() {
      const entities = this.originalEntities;
      const entityType2Color = this.entityType2Color;

      // 创建一个类型集合
      const typeSet = new Set();
      entities.forEach(entity => {
        typeSet.add(entity.type);
      });

      // 准备节点和连边的数据
      const nodes = [];
      const links = [];

      // 使用 Map 来跟踪已添加的节点，键为节点名称，值为节点对象
      const nodeMap = new Map();

      // 添加类型节点
      typeSet.forEach(type => {
        const typeNode = {
          name: type,
          category: type,
          symbolSize: 50,
          itemStyle: {
            color: entityType2Color[type] || '#000',
          },
          label: {
            show: true,
            formatter: '{b}',
            fontSize: 16,
          },
        };
        nodes.push(typeNode);
        nodeMap.set(type, typeNode);
      });

      // 添加实体节点和连边
      entities.forEach(entity => {
        const entityName = entity.entity;
        const entityType = entity.type;

        // 检查实体节点是否已存在
        if (!nodeMap.has(entityName)) {
          const entityNode = {
            name: entityName,
            category: entityType,
            symbolSize: 30,
            itemStyle: {
              color: entityType2Color[entityType] || '#000',
            },
            label: {
              show: true,
              formatter: '{b}',
              fontSize: 14,
            },
          };
          nodes.push(entityNode);
          nodeMap.set(entityName, entityNode);

          // 添加连边
          links.push({
            source: entityType,
            target: entityName,
          });
        } else {
          // 如果节点已存在，检查是否需要添加新的连边
          // 如果同一实体可能属于多个类型，可以在这里处理
          // 但根据您的需求，同一实体只属于一个类型，因此无需重复添加连边
        }
      });

      // 配置 ECharts 的图表选项
      this.chartOption = {
        tooltip: {},
        legend: {
          data: Array.from(typeSet),
        },
        series: [
          {
            name: '知识图谱',
            type: 'graph',
            layout: 'force',
            categories: Array.from(typeSet).map(type => ({
              name: type,
              itemStyle: {
                color: entityType2Color[type] || '#000',
              },
            })),

            data: nodes,
            links: links,
            roam: true,
            label: {
              position: 'right',
              formatter: '{b}',
            },
            force: {
              repulsion: 1000,
              edgeLength: [50, 200],
            },
          },
        ],
      };
    },


    // ...其余 methods
    // 关闭关系结果弹窗
    closeREResultPopup() {
      this.showREResult = false;
      this.reResult = null;
      this.reChartOption = null;
    },
    // 处理关系抽取的图谱数据
    // 在 processREGraphData 方法中
    // 处理关系抽取图谱数据
    processREGraphData() {
      if (!this.reResult || !Array.isArray(this.reResult)) return;

      const nodes = [];
      const links = [];
      const typeColors = {
        'Entity': this.entityType2Color['Entity'],
      };

      // 使用 Set 来跟踪已添加的实体节点，防止重复
      const entitySet = new Set();

      this.reResult.forEach((relation, index) => {
        const { start_entity, end_entity, type } = relation;

        // 添加起始实体节点
        if (!entitySet.has(start_entity)) {
          nodes.push({
            name: start_entity,
            category: 'Entity', // 所有实体节点均属于 'Entity'
            symbolSize: 50,
            itemStyle: {
              color: typeColors['Entity'],
            },
            label: {
              show: true,
              formatter: '{b}',
              fontSize: 14,
            },
          });
          entitySet.add(start_entity);
        }

        // 添加结束实体节点
        if (!entitySet.has(end_entity)) {
          nodes.push({
            name: end_entity,
            category: 'Entity', // 所有实体节点均属于 'Entity'
            symbolSize: 50,
            itemStyle: {
              color: typeColors['Entity'],
            },
            label: {
              show: true,
              formatter: '{b}',
              fontSize: 14,
            },
          });
          entitySet.add(end_entity);
        }

        // 添加关系连线
        links.push({
          source: start_entity,
          target: end_entity,
          label: {
            show: true,
            formatter: type, // 显示关系类型
            fontSize: 12,
            color: '#333', // 关系类型文字颜色
          },
          lineStyle: {
            type: 'solid',
            color: '#aaa', // 关系线颜色
          },
        });
      });

      // 配置 ECharts 的图表选项
      this.reChartOption = {
        tooltip: {
          formatter: function (params) {
            if (params.dataType === 'node') {
              return `${params.data.name}`;
            } else if (params.dataType === 'edge') {
              return `${params.data.label.formatter}`;
            }
            return '';
          },
        },
        series: [
          {
            name: '关系要素知识图谱',
            type: 'graph',
            layout: 'force',
            categories: [
              { name: 'Entity', itemStyle: { color: typeColors['Entity'] } }, // 仅实体类别
            ],
            data: nodes, // 实体节点
            links: links, // 关系连线
            roam: true,
            label: {
              position: 'right',
              formatter: '{b}',
            },
            force: {
              repulsion: 1000,
              edgeLength: [100, 200],
            },
            lineStyle: {
              width: 2,
              color: '#aaa',
              curveness: 0,
            },
            emphasis: {
              focus: 'adjacency',
              lineStyle: {
                width: 10
              }
            },
          },
        ],
      };

      // 更新图表
      if (this.chartInstance) {
        this.chartInstance.setOption(this.reChartOption, true);
      }
    },

// 在 mounted 或其他适当的生命周期钩子中初始化图表实例
    mounted() {
      this.fetchData();

      this.$nextTick(() => {
        // 初始化 ECharts 实例
        this.chartInstance = this.$refs.reChart.getEchartsInstance();

        // 监听 legend 事件以手动处理连线显示/隐藏（如果需要）
        this.chartInstance.on('legendselectchanged', params => {
          const selected = params.selected;
          // 遍历所有连线，根据节点的可见性调整连线的显示
          const updatedLinks = this.reChartOption.series[0].links.map(link => {
            const sourceCategory = this.reChartOption.series[0].data.find(node => node.name === link.source)?.category;
            const targetCategory = this.reChartOption.series[0].data.find(node => node.name === link.target)?.category;
            const sourceVisible = selected[sourceCategory];
            const targetVisible = selected[targetCategory];
            return {
              ...link,
              lineStyle: {
                ...link.lineStyle,
                opacity: sourceVisible && targetVisible ? 1 : 0,
              },
            };
          });
          this.reChartOption.series[0].links = updatedLinks;
          this.chartInstance.setOption(this.reChartOption, false);
        });
      });
    },





    // 关闭模型结果弹窗
    closeResultPopup() {
      this.showResultPopup = false; // 隐藏弹窗
    },

    // 处理 NER 模型选择变化
    selectModelNER(selectedModel) {
      this.selectModel(selectedModel, 'NER');
    },

    // 处理 RE 模型选择变化
    selectModelRE(selectedModel) {
      this.selectModel(selectedModel, 'RE');
    },

    // 处理 EE 模型选择变化
    selectModelEE(selectedModel) {
      this.selectModel(selectedModel, 'EE');
    },

    // 选择模型的方法
    selectModel(modelName, modelType) {
      // 根据模型类型设置选中状态，并重置其他模型库的选中状态
      if (modelType === 'NER') {
        this.selectedNERModel = modelName;
        this.selectedREModel = null;
        this.selectedEEModel = null;
      } else if (modelType === 'RE') {
        this.selectedREModel = modelName;
        this.selectedNERModel = null;
        this.selectedEEModel = null;
      } else if (modelType === 'EE') {
        this.selectedEEModel = modelName;
        this.selectedNERModel = null;
        this.selectedREModel = null;
      }

      // 根据模型类型查找正确的模型数据
      let modelData = null;
      if (modelType === 'NER') {
        modelData = this.allModelData.find(item => item.taskEnName === 'NER');
      } else if (modelType === 'RE') {
        modelData = this.allModelData.find(item => item.taskEnName === 'RE');
      } else if (modelType === 'EE') {
        modelData = this.allModelData.find(item => item.taskEnName === 'EE');
      }

      // 查找选中模型名称对应的模型 API
      if (modelData) {
        const selectedModelData = modelData.modelList.find(model => model.modelName === modelName);
        if (selectedModelData) {
          this.modelApi = selectedModelData.modelApi; // 设置正确的模型 API
          this.selectedModel = modelData.taskName;    // 设置选中模型的任务名称
        } else {
          console.error("未找到指定的模型名称");
        }
      } else {
        console.error("未找到指定的模型类型");
      }
    },

    // 关闭模型
    closeModel() {
      this.activeModel = null;
      this.selectedNERModel = null;
      this.selectedREModel = null;
      this.selectedEEModel = null;
      this.modelApi = null;
    },

  }
};
</script>

<style rel="stylesheet/scss" lang="scss">
.background_image {
  height: 100vh; /* 元素的高度设置为视口的高度（100% 的视口高度）*/
  background-image: url('../../assets/images/headpage_img/shouye1.jpg'); /* 设置背景图，路径指向项目中的图片 */
  background-size: cover; /* 背景图按比例缩放，以完全覆盖背景区域 */
  background-position: center; /* 背景图在容器中居中对齐 */
  z-index: -1; /* 确保背景图位于所有其他内容的下层 */
}
.content_box {
  position: relative; /* 使 div 相对于父元素可定位 */
  top: 100px; /* 向下移动 100px */
  left: 100px; /* 向右移动 100px */
}
.four_display {
  width: 800px;        /* 固定宽度 */
  height: 100px;       /* 固定高度 */
  //background-color: #d3d3d3; /* 背景颜色 */
  margin: 180px 0 0 100px; /* 顺序为：上 右 下 左 */
}
.one_display {
  margin: 0 25px 0 0; /* 顺序为：上 右 下 左 */
}

h1 {
  margin: 0;
  padding: 0;
  font-family: 'PingFang SC', sans-serif;
  font-size: 60px ;
  font-weight: bold; /* 设置字体加粗 */
}
h2 {
  font-family: 'PingFang SC', sans-serif;
  font-size: 30px ;
}

.switch-buttons {
  position: absolute;
  top: 50%; /* 垂直居中 */
  width: 100%;
  display: flex;
  justify-content: space-between;
  transform: translateY(-50%); /* 垂直居中 */
}
.switch-icon {
  cursor: pointer;
  width: 40px;  /* 设置图片的大小 */
  height: 40px; /* 设置图片的大小 */
  transition: transform 0.3s ease;
}

.switch-icon:hover {
  transform: scale(1.2); /* 鼠标悬停时放大 */
}
/* 右箭头的位置调整，位于 page1 的右侧 */
.right-arrow {
  position: absolute;
  right: 0; /* 右侧对齐 */
}

/* 左箭头的位置调整，位于 page2 的左侧 */
.left-arrow {
  position: absolute;
  left: 0; /* 左侧对齐 */
}




.page1_txbox {
  width: 400px;        /* 固定宽度 */
  height: 150px;       /* 固定高度 */
  //background-color: #d3d3d3; /* 背景颜色 */
  margin: 50px auto 0;    /* 上边距 100px，左右居中，底部 0 */
}
.yonghu {
  margin: 0;
  padding: 0;
  display: inline;
  font-size: 40px;
  color: #6484E5;
  font-weight: bold;
}
.nihao {
  margin: 0;
  padding: 0;
  display: inline;
  font-size: 40px;
  color: #BD6991;
  font-weight: bold;
}
.can_help {
  margin: 0;
  padding: 0;
  font-size: 40px;
  color: #101010;
  font-weight: bold;
}

.big_box {
  width: 1150px;
  height: 350px;
  //background-color: #d3d3d3;
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 0 auto;
}

.little_box {
  width: 350px;
  height: 350px;
  background-color: #F8FAFD;
  display: flex;
  flex-direction: column;
  justify-content: flex-start; /* 从垂直方向顶部开始排列 */
  align-items: center; /* 水平方向居中 */
  text-align: center;
  padding: 20px;
  box-sizing: border-box;
  position: relative;
}



.little_box h2 {
  margin: 0 0 20px 0; /* 添加底部间距 */
  font-weight: bold;
  font-size: 24px;
  font-family: 'PingFang SC', sans-serif;
  width: 100%; /* 使标题占满容器的宽度 */
  text-align: center; /* 中心对齐标题 */
}


.model_box {
  width: 1350px;
  height: 350px;
  background-color: #F8FAFD;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  padding: 20px;
  box-sizing: border-box;
  z-index: 1000;
}

.model_header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.model_header h2 {
  margin: 0;
  font-size: 24px;
  font-weight: bold;
}

.model_header button {
  background: none;
  border: none;
  font-size: 24px;
  cursor: pointer;
}

.model_content {
  margin-top: 20px;
}

/* 搜索栏样式 */
.search-bar {
  display: flex;
  justify-content: center;  /* 水平居中 */
  margin-top: 100px;
}

.search-input {
  width: 700px;             /* 设置输入框宽度 */
  height: 40px;             /* 设置输入框高度 */
  padding: 0 10px;          /* 内边距，增加左右空间 */
  font-size: 16px;          /* 设置字体大小 */
  border: 1px solid #ccc;  /* 边框 */
  border-radius: 20px;      /* 圆角效果 */
}

.search-button {
  margin-left: 10px;        /* 按钮和输入框之间的间距 */
  padding: 10px 20px;       /* 按钮的内边距 */
  font-size: 16px;          /* 按钮文字大小 */
  background-color: #0066cc; /* 按钮背景色 */
  color: white;             /* 按钮文字颜色 */
  border: none;             /* 去掉边框 */
  border-radius: 20px;      /* 圆角效果 */
  cursor: pointer;         /* 鼠标悬停时变为手指图标 */
  transition: background-color 0.3s; /* 按钮背景色变化过渡 */
}

.search-button:hover {
  background-color: #005bb5; /* 悬停时按钮的背景色 */
}

.list-section {
  width: 1110px;
  height: 250px;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 0 auto;
}

.list-section ul {
  list-style: none;
  padding: 0;
}

.list-section li {
  padding: 20px;
  cursor: pointer;
  transition: background-color 0.3s;
  text-align: center;
}

.list-section li:hover {
  background-color: #f0f0f0;
}

.pagination {
  display: flex;
  justify-content: center;
  gap: 10px;
}

button {
  padding: 8px 16px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

button:disabled {
  background-color: #c0c0c0;
  cursor: not-allowed;
}

.model-result {
  margin-top: 20px;
  padding: 10px;
  background-color: #f8fafc;
  border-radius: 10px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}

.model-result ul {
  list-style-type: none;
  padding: 0;
}

.model-result li {
  padding: 10px;
  background-color: #f0f0f0;
  margin-bottom: 5px;
  border-radius: 5px;
}

.selected-model {
  font-weight: bold;               /* 加粗字体 */
  color: white;                    /* 修改文字颜色为白色，以便在蓝色背景上清晰显示 */
  background-color: #2f6bec;       /* 蓝色背景 */
  border-radius: 5px;              /* 可选：圆角效果 */
}




/* 弹窗容器 */
.result-popup {
  width: 1350px;
  height: 900px;
  background-color: #ffffff;
  border: 1px solid #cccccc;
  border-radius: 10px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1000;
  display: flex; /* 使用 Flex 布局 */
  flex-direction: column; /* 子元素垂直排列 */
  font-family: Arial, sans-serif;
}

/* 弹窗头部 */
.result-popup-header {
  background-color: #f8f9fa;
  padding: 15px 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid #ddd;
}

.result-popup-header h2 {
  margin: 0;
  font-size: 18px;
  font-weight: bold;
  color: #333333;
}

.result-popup-close {
  background: none;
  border: none;
  font-size: 20px;
  color: #888888;
  cursor: pointer;
  transition: color 0.3s;
}

.result-popup-close:hover {
  color: #ff0000;
}

/* 弹窗内容区域 */
.result-popup-content {
  flex: 1; /* 让内容区域自动撑满剩余空间 */
  overflow-y: auto; /* 添加滚动条支持 */
  padding: 20px;
}

.result-popup-content ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

.result-popup-item {
  background-color: #f5f5f5;
  margin-bottom: 10px;
  padding: 15px;
  border-radius: 5px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}

.result-popup-item p {
  margin: 5px 0;
  font-size: 14px;
  color: #333333;
}

.result-popup-item p strong {
  color: #555555;
}

/* 原始句子区域 */
.result-popup-original {
  background-color: #f8f9fa;
  padding: 15px 20px;
  border-bottom: 1px solid #ddd;
}

.result-popup-original p {
  margin: 0;
  font-size: 16px;
  color: #333333;
}

.paragraph {
  display: flex;
  padding: .25rem .25rem 1rem .75rem;
  background: #ecf0f1;
  border-radius: 7px;
  margin-bottom: .25rem;
  flex-wrap: wrap;
  align-items: baseline;
}

.entity {
  margin-left: .25rem;
  margin-right: .25rem;
  min-width: 2rem;
  line-height: 1rem;
  padding: 0 0 .55rem 0;
  text-align: center;
  position: relative;
  display: flex;
  flex-shrink: 0;
  flex-grow: 0;
  justify-content: center;
}
.entity.last {
  padding: 0;
}
.entity::after {
  content: attr(text);
  font-size: .75rem;
  width: 100%;
  position: absolute;
  border-top-width: 2px;
  border-top-style: solid;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(-100% - 5px);
}

.text {
  flex-shrink: 0;
  flex-grow: 0;
}



/* 事件结果弹窗容器 */
.event-result-popup {
  width: 1350px;
  max-height: 90vh; /* 设置最大高度为视口高度的90%，防止溢出 */
  background-color: #ffffff;
  border: 1px solid #cccccc;
  border-radius: 10px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1000;
  display: flex; /* 使用 Flex 布局 */
  flex-direction: column; /* 子元素垂直排列 */
  font-family: Arial, sans-serif;
  overflow-y: auto; /* 允许弹窗内容滚动 */
}

/* 事件结果弹窗头部 */
.event-popup-header {
  background-color: #f8f9fa;
  padding: 15px 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid #ddd;
}

.event-popup-header h2 {
  margin: 0;
  font-size: 18px;
  font-weight: bold;
  color: #333333;
}

.event-popup-close {
  background: none;
  border: none;
  font-size: 20px;
  color: #888888;
  cursor: pointer;
  transition: color 0.3s;
}

.event-popup-close:hover {
  color: #ff0000;
}

/* 事件结果弹窗内容区域 - 照抄实体要素抽取内容区域样式 */
.event-popup-content {
  flex: 1; /* 让内容区域自动撑满剩余空间 */
  overflow-y: auto; /* 添加滚动条支持 */
  padding: 20px;
}

/* 如果实体弹窗有特定的项目样式，确保事件弹窗也有相同样式 */
.event-popup-content .event-item {
  margin-bottom: 10px; /* 保持一致 */
  background-color: #f5f5f5; /* 与实体弹窗一致 */
  padding: 15px; /* 与实体弹窗一致 */
  border-radius: 5px; /* 与实体弹窗一致 */
}

.event-popup-content .event-item p {
  margin: 5px 0;
  font-size: 14px; /* 根据实体弹窗调整 */
  color: #333333;
}

.event-popup-content .event-item p strong {
  color: #555555; /* 根据实体弹窗调整 */
}

/* 知识图谱展示部分 */
.v-chart {
  height: 800px; /* 调整高度以适应更多节点 */
  width: 100%;
  margin-top: 20px;
}


/* 关系结果弹窗容器 */
.re-result-popup {
  width: 1350px;
  max-height: 90vh; /* 设置最大高度为视口高度的90%，防止溢出 */
  background-color: #ffffff;
  border: 1px solid #cccccc;
  border-radius: 10px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1000;
  display: flex; /* 使用 Flex 布局 */
  flex-direction: column; /* 子元素垂直排列 */
  font-family: Arial, sans-serif;
  overflow-y: auto; /* 允许弹窗内容滚动 */
}

/* 继承现有弹窗的头部和内容样式 */
.re-result-popup .result-popup-header {
  background-color: #f8f9fa;
  padding: 15px 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid #ddd;
}

.re-result-popup .result-popup-header h2 {
  margin: 0;
  font-size: 18px;
  font-weight: bold;
  color: #333333;
}

.re-result-popup .result-popup-close {
  background: none;
  border: none;
  font-size: 20px;
  color: #888888;
  cursor: pointer;
  transition: color 0.3s;
}

.re-result-popup .result-popup-close:hover {
  color: #ff0000;
}

.re-result-popup .result-popup-original {
  background-color: #f8f9fa;
  padding: 15px 20px;
  border-bottom: 1px solid #ddd;
}

.re-result-popup .result-popup-original p {
  margin: 0;
  font-size: 16px;
  color: #333333;
}

.re-result-popup .result-popup-content {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
}

.re-result-popup .triplet {
  margin-right: 20px; /* 根据需要调整间距 */
  white-space: nowrap;
}

.list-section ul {
  list-style: none;
  padding: 0;
}

/* 添加对 Element UI 单选框的样式调整 */
.little_box .el-radio-group {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.little_box .el-radio {
  margin-bottom: 10px;
  font-size: 32px;
}

/* 可选：自定义选中状态的样式 */
.little_box .el-radio.is-checked .el-radio__label {
  color: #2f6bec; /* 与之前的选中颜色保持一致 */
  font-weight: bold;
}



</style>
